Previous script: “06_get_eigengene_QTL.Rmd”

The goal is to find QTL peaks for the WGCNA eigen genes and see if those overalp with any growth QTL. We are only focusing on eigen genes that correlated with some growth traits/paramters.

library(GenomicRanges)
library(qtl)
library(tidyverse)
library(stringr)
load("../output/scanone-eigengene-qtl_2012.RData")

scanone imp

Plot QTL

threshold.95 <- tibble(perm.threshold=lod.thrs[5,],
                       trait=colnames(lod.thrs))
scanone.gather <- scanone_eigen %>%
  gather(key = trait, value = LOD, -chr, -pos) %>%
  mutate(condition=str_sub(trait,1,2), color=str_sub(trait,6,100)) %>%
  left_join(threshold.95)
Joining, by = "trait"
scanone.gather

rr pl.UN <- scanone.gather %>% filter(condition==) %>% ggplot(aes(x=pos,y=LOD)) + geom_line() + geom_hline(aes(yintercept=perm.threshold),lty=2,lwd=.5,alpha=.5) + facet_grid(trait ~ chr, scales=) + theme(strip.text.y = element_text(angle=0), axis.text.x = element_text(angle=90)) + ggtitle(Eigen Gene QTL) pl.UN ggsave(../output/eigen gene eQTL UN 2012.pdf,width=12,height=8)

Look for overlap

For each eigen gene, find QTL borders and look for overlap with growth QTL

For each eigen gene first identify chromosomes with “significant” peaks (in this case > 99% permuation threshold) and then run bayesint() on them to define the intervals

sig.chrs <- scanone.gather %>% filter(LOD > perm.threshold) %>%
  group_by(trait,chr) %>%
  summarize(unique(chr))
sig.chrs

now for each significant chromosome/trait combo run bayesint

scanone_eigen.phys <- scanone_eigen[!str_detect(rownames(scanone_eigen),"^cA"),]
bayesint.list <- apply(sig.chrs,1,function(hit) {
    result <- bayesint(scanone_eigen.phys[c("chr","pos",hit["trait"])], 
                     chr=hit["chr"], 
                     lodcolumn = 1,
                     expandtomarkers = TRUE
  )
  colnames(result)[3] <- "LOD"
  result
})
names(bayesint.list) <- sig.chrs$trait
bayesint.list <- lapply(bayesint.list,function(x) x %>% 
                          as.data.frame() %>%
                          rownames_to_column(var="markername")  %>%
                          mutate(chr=as.character(chr))
)
bayesint.result <- as.tibble(bind_rows(bayesint.list,.id="trait")) %>% 
  select(trait,chr,pos,markername,LOD) %>%
  separate(markername,into=c("chr1","Mbp"),sep="x", convert=TRUE) %>%
  group_by(trait,chr) %>% 
  summarize(start=min(Mbp),end=max(Mbp),min_eQTL_LOD=min(LOD),max_eQTL_LOD=max(LOD)) %>% 
  #for the high QTL peaks the interval width is 0.  That is overly precise and need to widen those.
  mutate(start=ifelse(start==end,max(0,start-20000),start), end=ifelse(start==end,end+20000,end))
  
  
bayesint.result

annotate Eigen gene QTL

Load annotation

BrapaAnnotation <- read_csv("../input/Brapa_V1.5_annotated.csv")
Missing column names filled in: 'X1' [1]Parsed with column specification:
cols(
  X1 = col_integer(),
  name = col_character(),
  chrom = col_character(),
  start = col_integer(),
  end = col_integer(),
  subject = col_character(),
  AGI = col_character(),
  At_symbol = col_character(),
  At_description = col_character(),
  perc_ID = col_double(),
  aln_length = col_integer(),
  mismatch = col_integer(),
  gap_open = col_integer(),
  qstart = col_integer(),
  qend = col_integer(),
  sstart = col_integer(),
  send = col_integer(),
  eval = col_double(),
  score = col_double()
)
BrapaAnnotation
eigen.annotated <- lapply(1:nrow(bayesint.result),function(row) {
  qtl <- bayesint.result[row,]
  results <- subset(BrapaAnnotation, chrom==qtl$chr &
                    start >= qtl$start &
                    end <= qtl$end)
}
)
names(eigen.annotated) <- bayesint.result$trait
eigen.annotated <- bind_rows(eigen.annotated,.id="trait") %>%
  mutate(chrom=as.character(chrom)) %>%
  left_join(bayesint.result,by=c("trait","chrom"="chr")) %>% #get eQTL LOD
  rename(eigen_eQTL_candidate=name)
eigen.annotated.small <- eigen.annotated %>% select(trait,eigen_eQTL_candidate,ends_with("LOD"))
eigen.annotated.small
write_csv(eigen.annotated.small, 
          path=str_c("../output/", filebase, "_eigenQTL_ALL_", Sys.Date(), ".csv"))

given bayesint results, find overlaps with UN growth QTL

filepath <- "../input/All2012HeightQTL2.xlsx"
filebase <- filepath %>% basename() %>% str_replace("\\..*$","")
QTLgenes <- readxl::read_excel(filepath)[,-1]
QTLgenes <- QTLgenes %>% dplyr::rename(.id=QTL, FVTtrait=FVT) # change names to match previous file
QTLgenes <- QTLgenes %>% filter(str_detect(FVTtrait,"^UN"))
QTLgenes

rr eigen.qtl.combined <- inner_join(eigen.annotated.small,QTLgenes,by=c(_eQTL_candidate=)) %>% select(.id, trait, everything()) eigen.qtl.combined

how many QTL have at least some overlap?

rr sort(unique(QTLgenes$.id))

 [1] \QTL1\  \QTL12\ \QTL13\ \QTL14\ \QTL15\ \QTL16\ \QTL17\ \QTL18\ \QTL19\ \QTL2\  \QTL3\  \QTL33\
[13] \QTL34\ \QTL35\ \QTL6\  \QTL7\ 

rr sort(unique(eigen.qtl.combined$.id))

[1] \QTL1\  \QTL13\ \QTL14\ \QTL19\ \QTL3\  \QTL35\ \QTL6\  \QTL7\ 

three of four

are all eigen genes overlapping?

rr unique(eigen.annotated.small$trait)

 [1] \UN_MEblue\          \UN_MEbrown\         \UN_MEcyan\          \UN_MEdarkslateblue\
 [5] \UN_MElightgreen\    \UN_MEmidnightblue\  \UN_MEpurple\        \UN_MEsteelblue\    
 [9] \UN_MEturquoise\     \UN_MEyellow\        \UN_MEyellowgreen\  

rr unique(eigen.qtl.combined$trait)

[1] \UN_MEblue\          \UN_MEbrown\         \UN_MEcyan\          \UN_MEdarkslateblue\
[5] \UN_MEmidnightblue\  \UN_MEpurple\        \UN_MEturquoise\     \UN_MEyellow\       
[9] \UN_MEyellowgreen\  

No, 7 of 11

rr write_csv(eigen.qtl.combined, path=str_c(../output/, filebase, _eigenQTL_overlap_, Sys.Date(), .csv))

overlaps and significance

first convert things to ranges

rr qtl.info <- QTLgenes %>% group_by(.id) %>% summarize(chrom=unique(chrom),start=min(start),end=max(end)) qtl.info r qtl.ranges <- GRanges(seqnames = qtl.info\(chrom,ranges=IRanges(start=qtl.info\)start,end=qtl.info$end)) qtl.ranges <- GenomicRanges::reduce(qtl.ranges)

rr eQTL.ranges <- GRanges(bayesint.result\(chr, ranges = IRanges(start=bayesint.result\)start, end=bayesint.result$end)) eQTL.ranges <- GenomicRanges::reduce(eQTL.ranges)

Make table of chromosome info

rr chr.info <- scanone_eigen.phys %>% as.data.frame() %>% rownames_to_column() %>% select(marker) %>% separate(marker,into=c(,),sep=,convert=TRUE) %>% group_by(chr) %>% summarize(start=min(bp),end=max(bp))

Do the simulations

rr sims <- 1000 set.seed(2323) sim.results <- sapply(1:sims, function(s) { if (s %% 100 == 0) print(s) sim.eQTL <- tibble( chr=sample(chr.info\(chr, size = length(eQTL.ranges), replace = TRUE, prob=chr.info\)end/sum(chr.info\(end)), width=width(eQTL.ranges) # width of the QTL to simulate ) sim.eQTL <- chr.info %>% select(chr,chr.start=start,chr.end=end) %>% right_join(sim.eQTL,by=\chr\) #need to get the chrom end so we can sample correctly sim.eQTL <- sim.eQTL %>% mutate(qtl.start = runif(n=n(), min = chr.start, max= max(chr.start,chr.end-width)), qtl.end=qtl.start+width) sim.eQTL.ranges <- GRanges(seqnames = sim.eQTL\)chr, ranges = IRanges(start=sim.eQTL\(qtl.start, end=sim.eQTL\)qtl.end))

suppressWarnings(result <- sum(countOverlaps(qtl.ranges,sim.eQTL.ranges)>0)) result })

[1] 100
[1] 200
[1] 300
[1] 400
[1] 500
[1] 600
[1] 700
[1] 800
[1] 900
[1] 1000

rr true.overlap <- sum(countOverlaps(qtl.ranges,eQTL.ranges)) #OK to ignore warnings

Each of the 2 combined objects has sequence levels not in the other:
  - in 'x': A09, A05
  - in 'y': A08
  Make sure to always combine/compare objects based on the same reference
  genome (use suppressWarnings() to suppress this warning).

rr true.overlap

[1] 4

rr mean(sim.results >= true.overlap)

[1] 0.065

rr tibble(FVTQTL_vs_MReQTL_True_Overlaps=true.overlap, N_Simulations_fewer_overlaps=sum(sim.results < true.overlap), N_Simulations_greater_equal_overlaps=sum(sim.results >= true.overlap), P_value=mean(sim.results >= true.overlap) ) %>% write_csv(str_c(../output/, filebase, _WGCNA_eigen_eQTL_scanone_overlap_pval_, Sys.Date(), .csv))

cim

Plot QTL

rr threshold.95 <- tibble(perm.threshold=lod.thrs.cim[5,], trait=colnames(lod.thrs.cim)) scanone.gather <- scanone_eigen_cim %>% gather(key = trait, value = LOD, -chr, -pos) %>% mutate(condition=str_sub(trait,1,2), color=str_sub(trait,6,100)) %>% left_join(threshold.95)

Joining, by = \trait\

rr scanone.gather

rr pl.UN <- scanone.gather %>% filter(condition==) %>% ggplot(aes(x=pos,y=LOD)) + geom_line() + geom_hline(aes(yintercept=perm.threshold),lty=2,lwd=.5,alpha=.5) + facet_grid(trait ~ chr, scales=) + theme(strip.text.y = element_text(angle=0), axis.text.x = element_text(angle=90)) + ggtitle(Eigen Gene QTL) pl.UN ggsave(../output/eigen gene eQTL UN CIM 2012.pdf,width=12,height=8)

Look for overlap

For each eigen gene, find QTL borders and look for overlap with growth QTL

For each eigen gene first identify chromosomes with “significant” peaks (in this case > 99% permuation threshold) and then runs bayesint() on them to define the intervals

rr sig.chrs <- scanone.gather %>% filter(LOD > perm.threshold) %>% group_by(trait,chr) %>% summarize(unique(chr)) sig.chrs

now for each significant chromosome/trait combo run bayesint

rr #remove markers without physical position scanone_eigen_cim.phys <- scanone_eigen_cim[!str_detect(rownames(scanone_eigen),^cA),] bayesint.list <- apply(sig.chrs,1,function(hit) { result <- bayesint(scanone_eigen_cim.phys[c(,,hit[])], chr=hit[], lodcolumn = 1, expandtomarkers = TRUE ) colnames(result)[3] <-
result }) names(bayesint.list) <- sig.chrs$trait bayesint.list <- lapply(bayesint.list,function(x) x %>% as.data.frame() %>% rownames_to_column(var=) %>% mutate(chr=as.character(chr)) ) bayesint.result <- as.tibble(bind_rows(bayesint.list,.id=)) %>% select(trait,chr,pos,markername,LOD) %>% separate(markername,into=c(1,),sep=, convert=TRUE) %>% group_by(trait,chr) %>% summarize(start=min(Mbp),end=max(Mbp),min_eQTL_LOD=min(LOD),max_eQTL_LOD=max(LOD)) %>% #for the high QTL peaks the interval width is 0. That is overly precise and need to widen those. mutate(start=ifelse(start==end,max(0,start-20000),start), end=ifelse(start==end,end+20000,end))

bayesint.result

annotate Eigen gene QTL

Load annotation

rr BrapaAnnotation <- read_csv(../input/Brapa_V1.5_annotated.csv)

Missing column names filled in: 'X1' [1]Parsed with column specification:
cols(
  X1 = col_integer(),
  name = col_character(),
  chrom = col_character(),
  start = col_integer(),
  end = col_integer(),
  subject = col_character(),
  AGI = col_character(),
  At_symbol = col_character(),
  At_description = col_character(),
  perc_ID = col_double(),
  aln_length = col_integer(),
  mismatch = col_integer(),
  gap_open = col_integer(),
  qstart = col_integer(),
  qend = col_integer(),
  sstart = col_integer(),
  send = col_integer(),
  eval = col_double(),
  score = col_double()
)

|============                                                                                      |  13%
|=============                                                                             |  14%    1 MB
|==============                                                                            |  15%    1 MB
|===============                                                                           |  16%    1 MB
|================                                                                          |  17%    1 MB
|=================                                                                         |  19%    1 MB
|==================                                                                        |  20%    1 MB
|===================                                                                       |  21%    1 MB
|====================                                                                      |  22%    1 MB
|=====================                                                                     |  24%    1 MB
|=======================                                                                   |  25%    1 MB
|========================                                                                  |  26%    1 MB
|=========================                                                                 |  27%    2 MB
|==========================                                                                |  29%    2 MB
|===========================                                                               |  30%    2 MB
|============================                                                              |  31%    2 MB
|=============================                                                             |  32%    2 MB
|===============================                                                           |  34%    2 MB
|================================                                                          |  35%    2 MB
|=================================                                                         |  36%    2 MB
|==================================                                                        |  37%    2 MB
|===================================                                                       |  38%    2 MB
|====================================                                                      |  40%    2 MB
|=====================================                                                     |  41%    3 MB
|======================================                                                    |  42%    3 MB
|=======================================                                                   |  43%    3 MB
|========================================                                                  |  44%    3 MB
|==========================================                                                |  46%    3 MB
|===========================================                                               |  47%    3 MB
|============================================                                              |  48%    3 MB
|=============================================                                             |  49%    3 MB
|==============================================                                            |  51%    3 MB
|===============================================                                           |  52%    3 MB
|================================================                                          |  53%    3 MB
|=================================================                                         |  54%    4 MB
|==================================================                                        |  55%    4 MB
|===================================================                                       |  57%    4 MB
|=====================================================                                     |  58%    4 MB
|======================================================                                    |  59%    4 MB
|=======================================================                                   |  60%    4 MB
|========================================================                                  |  61%    4 MB
|=========================================================                                 |  62%    4 MB
|==========================================================                                |  64%    4 MB
|===========================================================                               |  65%    4 MB
|============================================================                              |  66%    4 MB
|=============================================================                             |  67%    5 MB
|==============================================================                            |  69%    5 MB
|===============================================================                           |  70%    5 MB
|================================================================                          |  71%    5 MB
|==================================================================                        |  72%    5 MB
|===================================================================                       |  73%    5 MB
|====================================================================                      |  75%    5 MB
|=====================================================================                     |  76%    5 MB
|======================================================================                    |  77%    5 MB
|=======================================================================                   |  78%    5 MB
|========================================================================                  |  80%    5 MB
|=========================================================================                 |  81%    6 MB
|==========================================================================                |  82%    6 MB
|============================================================================              |  83%    6 MB
|=============================================================================             |  84%    6 MB
|==============================================================================            |  86%    6 MB
|===============================================================================           |  87%    6 MB
|================================================================================          |  88%    6 MB
|=================================================================================         |  89%    6 MB
|==================================================================================        |  90%    6 MB
|===================================================================================       |  92%    6 MB
|=====================================================================================     |  93%    6 MB
|======================================================================================    |  94%    7 MB
|=======================================================================================   |  95%    7 MB
|========================================================================================  |  97%    7 MB
|========================================================================================= |  98%    7 MB
|==========================================================================================|  99%    7 MB
|===========================================================================================| 100%    7 MB

rr BrapaAnnotation

rr eigen.annotated <- lapply(1:nrow(bayesint.result),function(row) { qtl <- bayesint.result[row,] results <- subset(BrapaAnnotation, chrom==qtl\(chr & start >= qtl\)start & end <= qtl\(end) } ) names(eigen.annotated) <- bayesint.result\)trait eigen.annotated <- bind_rows(eigen.annotated,.id=) %>% mutate(chrom=as.character(chrom)) %>% left_join(bayesint.result,by=c(,=)) %>% #get eQTL LOD rename(eigen_eQTL_candidate=name) eigen.annotated.small <- eigen.annotated %>% select(trait,eigen_eQTL_candidate,ends_with()) eigen.annotated.small

given bayesint results, find overlaps with UN growth QTL

rr filepath <- ../input/All2012HeightQTL2.xlsx
filebase <- filepath %>% basename() %>% str_replace(\..*$,\) QTLgenes <- readxl::read_excel(filepath)[,-1] QTLgenes <- QTLgenes %>% dplyr::rename(.id=QTL, FVTtrait=FVT) # change names to match previous file QTLgenes <- QTLgenes %>% filter(str_detect(FVTtrait,^UN)) QTLgenes

rr eigen.qtl.combined <- inner_join(eigen.annotated.small,QTLgenes,by=c(_eQTL_candidate=)) %>% select(.id, trait, everything()) eigen.qtl.combined

how many QTL have at least some overlap?

rr unique(QTLgenes$.id)

 [1] \QTL1\  \QTL12\ \QTL13\ \QTL14\ \QTL15\ \QTL16\ \QTL17\ \QTL18\ \QTL19\ \QTL2\  \QTL3\  \QTL33\
[13] \QTL34\ \QTL35\ \QTL6\  \QTL7\ 

rr unique(eigen.qtl.combined$.id)

[1] \QTL3\  \QTL7\  \QTL19\ \QTL1\  \QTL13\ \QTL6\ 

two of four

are all eigen genes overlapping?

rr unique(eigen.annotated.small$trait)

[1] \UN_MEblue\          \UN_MEbrown\         \UN_MEcyan\          \UN_MEdarkslateblue\
[5] \UN_MElightgreen\    \UN_MEmidnightblue\  \UN_MEsteelblue\     \UN_MEturquoise\    
[9] \UN_MEyellowgreen\  

rr unique(eigen.qtl.combined$trait)

[1] \UN_MEbrown\         \UN_MEcyan\          \UN_MEdarkslateblue\ \UN_MEmidnightblue\ 
[5] \UN_MEturquoise\     \UN_MEyellowgreen\  

No, 2

rr write_csv(eigen.qtl.combined, path=str_c(../output/, filebase, _eigenQTL_overlap_CIM_, Sys.Date(), .csv))

overlaps and signficance

rr eQTL.ranges <- GRanges(bayesint.result\(chr, ranges = IRanges(start=bayesint.result\)start, end=bayesint.result$end)) eQTL.ranges <- GenomicRanges::reduce(eQTL.ranges)

Do the simulations

rr sims <- 1000 set.seed(4545) sim.results <- sapply(1:sims, function(s) { if (s %% 100 == 0) print(s) sim.eQTL <- tibble( chr=sample(chr.info\(chr, size = length(eQTL.ranges), replace = TRUE, prob=chr.info\)end/sum(chr.info\(end)), width=width(eQTL.ranges) # width of the QTL to simulate ) sim.eQTL <- chr.info %>% select(chr,chr.start=start,chr.end=end) %>% right_join(sim.eQTL,by=\chr\) #need to get the chrom end so we can sample correctly sim.eQTL <- sim.eQTL %>% mutate(qtl.start = runif(n=n(), min = chr.start, max= max(chr.start,chr.end-width)), qtl.end=qtl.start+width) sim.eQTL.ranges <- GRanges(seqnames = sim.eQTL\)chr, ranges = IRanges(start=sim.eQTL\(qtl.start, end=sim.eQTL\)qtl.end))

suppressWarnings(result <- sum(countOverlaps(qtl.ranges,sim.eQTL.ranges)>0)) result })

[1] 100
[1] 200
[1] 300
[1] 400
[1] 500
[1] 600
[1] 700
[1] 800
[1] 900
[1] 1000

rr true.overlap <- sum(countOverlaps(qtl.ranges,eQTL.ranges)) #OK to ignore warnings true.overlap

[1] 4

rr mean(sim.results >= true.overlap)

[1] 0.005

rr tibble(FVTQTL_vs_MReQTL_True_Overlaps=true.overlap, N_Simulations_fewer_overlaps=sum(sim.results < true.overlap), N_Simulations_greater_equal_overlaps=sum(sim.results >= true.overlap), P_value=mean(sim.results >= true.overlap) ) %>% write_csv(str_c(../output/, filebase, _WGCNA_eigen_eQTL_CIM_overlap_pval_, Sys.Date(), .csv))

LS0tCnRpdGxlOiAiQW5hbHl6ZSBFaWdlbiBHZW5lIFFUTCIKb3V0cHV0OiBodG1sX25vdGVib29rCmF1dGhvcjogIkp1bGluIE1hbG9vZiIKLS0tCgoKUHJldmlvdXMgc2NyaXB0OiAiMDZfZ2V0X2VpZ2VuZ2VuZV9RVEwuUm1kIgoKVGhlIGdvYWwgaXMgdG8gZmluZCBRVEwgcGVha3MgZm9yIHRoZSBXR0NOQSBlaWdlbiBnZW5lcyBhbmQgc2VlIGlmIHRob3NlIG92ZXJhbHAgd2l0aCBhbnkgZ3Jvd3RoIFFUTC4gIFdlIGFyZSBvbmx5IGZvY3VzaW5nIG9uIGVpZ2VuIGdlbmVzIHRoYXQgY29ycmVsYXRlZCB3aXRoIHNvbWUgZ3Jvd3RoIHRyYWl0cy9wYXJhbXRlcnMuCgpgYGB7cn0KbGlicmFyeShHZW5vbWljUmFuZ2VzKQpsaWJyYXJ5KHF0bCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoc3RyaW5ncikKbG9hZCgiLi4vb3V0cHV0L3NjYW5vbmUtZWlnZW5nZW5lLXF0bF8yMDEyLlJEYXRhIikKYGBgCgojIHNjYW5vbmUgaW1wCgojIyBQbG90IFFUTAoKYGBge3J9Cgp0aHJlc2hvbGQuOTUgPC0gdGliYmxlKHBlcm0udGhyZXNob2xkPWxvZC50aHJzWzUsXSwKICAgICAgICAgICAgICAgICAgICAgICB0cmFpdD1jb2xuYW1lcyhsb2QudGhycykpCgpzY2Fub25lLmdhdGhlciA8LSBzY2Fub25lX2VpZ2VuICU+JQogIGdhdGhlcihrZXkgPSB0cmFpdCwgdmFsdWUgPSBMT0QsIC1jaHIsIC1wb3MpICU+JQogIG11dGF0ZShjb25kaXRpb249c3RyX3N1Yih0cmFpdCwxLDIpLCBjb2xvcj1zdHJfc3ViKHRyYWl0LDYsMTAwKSkgJT4lCiAgbGVmdF9qb2luKHRocmVzaG9sZC45NSkKCnNjYW5vbmUuZ2F0aGVyCmBgYAoKYGBge3J9CiAgIHBsLlVOIDwtIHNjYW5vbmUuZ2F0aGVyICU+JSBmaWx0ZXIoY29uZGl0aW9uPT0iVU4iKSAlPiUKICBnZ3Bsb3QoYWVzKHg9cG9zLHk9TE9EKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PXBlcm0udGhyZXNob2xkKSxsdHk9Mixsd2Q9LjUsYWxwaGE9LjUpICsKICBmYWNldF9ncmlkKHRyYWl0IH4gY2hyLCBzY2FsZXM9ImZyZWUiKSArCiAgdGhlbWUoc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTApLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCkpICsKICBnZ3RpdGxlKCJVTiBFaWdlbiBHZW5lIFFUTCIpCnBsLlVOCmdnc2F2ZSgiLi4vb3V0cHV0L2VpZ2VuIGdlbmUgZVFUTCBVTiAyMDEyLnBkZiIsd2lkdGg9MTIsaGVpZ2h0PTgpCmBgYAoKCiMjIExvb2sgZm9yIG92ZXJsYXAKCkZvciBlYWNoIGVpZ2VuIGdlbmUsIGZpbmQgUVRMIGJvcmRlcnMgYW5kIGxvb2sgZm9yIG92ZXJsYXAgd2l0aCBncm93dGggUVRMCgpGb3IgZWFjaCBlaWdlbiBnZW5lIGZpcnN0IGlkZW50aWZ5IGNocm9tb3NvbWVzIHdpdGggInNpZ25pZmljYW50IiBwZWFrcyAoaW4gdGhpcyBjYXNlID4gOTklIHBlcm11YXRpb24gdGhyZXNob2xkKSBhbmQgdGhlbiBydW4gYmF5ZXNpbnQoKSBvbiB0aGVtIHRvIGRlZmluZSB0aGUgaW50ZXJ2YWxzCgpgYGB7cn0Kc2lnLmNocnMgPC0gc2Nhbm9uZS5nYXRoZXIgJT4lIGZpbHRlcihMT0QgPiBwZXJtLnRocmVzaG9sZCkgJT4lCiAgZ3JvdXBfYnkodHJhaXQsY2hyKSAlPiUKICBzdW1tYXJpemUodW5pcXVlKGNocikpCnNpZy5jaHJzCmBgYAoKbm93IGZvciBlYWNoIHNpZ25pZmljYW50IGNocm9tb3NvbWUvdHJhaXQgY29tYm8gcnVuIGJheWVzaW50CgpgYGB7cn0Kc2Nhbm9uZV9laWdlbi5waHlzIDwtIHNjYW5vbmVfZWlnZW5bIXN0cl9kZXRlY3Qocm93bmFtZXMoc2Nhbm9uZV9laWdlbiksIl5jQSIpLF0KCmJheWVzaW50Lmxpc3QgPC0gYXBwbHkoc2lnLmNocnMsMSxmdW5jdGlvbihoaXQpIHsKICAgIHJlc3VsdCA8LSBiYXllc2ludChzY2Fub25lX2VpZ2VuLnBoeXNbYygiY2hyIiwicG9zIixoaXRbInRyYWl0Il0pXSwgCiAgICAgICAgICAgICAgICAgICAgIGNocj1oaXRbImNociJdLCAKICAgICAgICAgICAgICAgICAgICAgbG9kY29sdW1uID0gMSwKICAgICAgICAgICAgICAgICAgICAgZXhwYW5kdG9tYXJrZXJzID0gVFJVRQogICkKICBjb2xuYW1lcyhyZXN1bHQpWzNdIDwtICJMT0QiCiAgcmVzdWx0Cn0pCgpuYW1lcyhiYXllc2ludC5saXN0KSA8LSBzaWcuY2hycyR0cmFpdAoKYmF5ZXNpbnQubGlzdCA8LSBsYXBwbHkoYmF5ZXNpbnQubGlzdCxmdW5jdGlvbih4KSB4ICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgcm93bmFtZXNfdG9fY29sdW1uKHZhcj0ibWFya2VybmFtZSIpICAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoY2hyPWFzLmNoYXJhY3RlcihjaHIpKQopCgpiYXllc2ludC5yZXN1bHQgPC0gYXMudGliYmxlKGJpbmRfcm93cyhiYXllc2ludC5saXN0LC5pZD0idHJhaXQiKSkgJT4lIAogIHNlbGVjdCh0cmFpdCxjaHIscG9zLG1hcmtlcm5hbWUsTE9EKSAlPiUKICBzZXBhcmF0ZShtYXJrZXJuYW1lLGludG89YygiY2hyMSIsIk1icCIpLHNlcD0ieCIsIGNvbnZlcnQ9VFJVRSkgJT4lCiAgZ3JvdXBfYnkodHJhaXQsY2hyKSAlPiUgCiAgc3VtbWFyaXplKHN0YXJ0PW1pbihNYnApLGVuZD1tYXgoTWJwKSxtaW5fZVFUTF9MT0Q9bWluKExPRCksbWF4X2VRVExfTE9EPW1heChMT0QpKSAlPiUgCiAgI2ZvciB0aGUgaGlnaCBRVEwgcGVha3MgdGhlIGludGVydmFsIHdpZHRoIGlzIDAuICBUaGF0IGlzIG92ZXJseSBwcmVjaXNlIGFuZCBuZWVkIHRvIHdpZGVuIHRob3NlLgogIG11dGF0ZShzdGFydD1pZmVsc2Uoc3RhcnQ9PWVuZCxtYXgoMCxzdGFydC0yMDAwMCksc3RhcnQpLCBlbmQ9aWZlbHNlKHN0YXJ0PT1lbmQsZW5kKzIwMDAwLGVuZCkpCiAgCiAgCmJheWVzaW50LnJlc3VsdApgYGAKCiMjIyBhbm5vdGF0ZSBFaWdlbiBnZW5lIFFUTAoKTG9hZCBhbm5vdGF0aW9uCmBgYHtyfQpCcmFwYUFubm90YXRpb24gPC0gcmVhZF9jc3YoIi4uL2lucHV0L0JyYXBhX1YxLjVfYW5ub3RhdGVkLmNzdiIpCkJyYXBhQW5ub3RhdGlvbgpgYGAKCmBgYHtyfQplaWdlbi5hbm5vdGF0ZWQgPC0gbGFwcGx5KDE6bnJvdyhiYXllc2ludC5yZXN1bHQpLGZ1bmN0aW9uKHJvdykgewogIHF0bCA8LSBiYXllc2ludC5yZXN1bHRbcm93LF0KICByZXN1bHRzIDwtIHN1YnNldChCcmFwYUFubm90YXRpb24sIGNocm9tPT1xdGwkY2hyICYKICAgICAgICAgICAgICAgICAgICBzdGFydCA+PSBxdGwkc3RhcnQgJgogICAgICAgICAgICAgICAgICAgIGVuZCA8PSBxdGwkZW5kKQp9CikKbmFtZXMoZWlnZW4uYW5ub3RhdGVkKSA8LSBiYXllc2ludC5yZXN1bHQkdHJhaXQKCmVpZ2VuLmFubm90YXRlZCA8LSBiaW5kX3Jvd3MoZWlnZW4uYW5ub3RhdGVkLC5pZD0idHJhaXQiKSAlPiUKICBtdXRhdGUoY2hyb209YXMuY2hhcmFjdGVyKGNocm9tKSkgJT4lCiAgbGVmdF9qb2luKGJheWVzaW50LnJlc3VsdCxieT1jKCJ0cmFpdCIsImNocm9tIj0iY2hyIikpICU+JSAjZ2V0IGVRVEwgTE9ECiAgcmVuYW1lKGVpZ2VuX2VRVExfY2FuZGlkYXRlPW5hbWUpCgplaWdlbi5hbm5vdGF0ZWQuc21hbGwgPC0gZWlnZW4uYW5ub3RhdGVkICU+JSBzZWxlY3QodHJhaXQsZWlnZW5fZVFUTF9jYW5kaWRhdGUsZW5kc193aXRoKCJMT0QiKSkKCmVpZ2VuLmFubm90YXRlZC5zbWFsbAoKd3JpdGVfY3N2KGVpZ2VuLmFubm90YXRlZC5zbWFsbCwgCiAgICAgICAgICBwYXRoPXN0cl9jKCIuLi9vdXRwdXQvIiwgZmlsZWJhc2UsICJfZWlnZW5RVExfQUxMXyIsIFN5cy5EYXRlKCksICIuY3N2IikpCmBgYAoKZ2l2ZW4gYmF5ZXNpbnQgcmVzdWx0cywgZmluZCBvdmVybGFwcyB3aXRoIFVOIGdyb3d0aCBRVEwKCmBgYHtyfQpmaWxlcGF0aCA8LSAiLi4vaW5wdXQvQWxsMjAxMkhlaWdodFFUTDIueGxzeCIKZmlsZWJhc2UgPC0gZmlsZXBhdGggJT4lIGJhc2VuYW1lKCkgJT4lIHN0cl9yZXBsYWNlKCJcXC4uKiQiLCIiKQoKUVRMZ2VuZXMgPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKGZpbGVwYXRoKVssLTFdClFUTGdlbmVzIDwtIFFUTGdlbmVzICU+JSBkcGx5cjo6cmVuYW1lKC5pZD1RVEwsIEZWVHRyYWl0PUZWVCkgIyBjaGFuZ2UgbmFtZXMgdG8gbWF0Y2ggcHJldmlvdXMgZmlsZQpRVExnZW5lcyA8LSBRVExnZW5lcyAlPiUgZmlsdGVyKHN0cl9kZXRlY3QoRlZUdHJhaXQsIl5VTiIpKQpRVExnZW5lcwpgYGAKCmBgYHtyfQplaWdlbi5xdGwuY29tYmluZWQgPC0gaW5uZXJfam9pbihlaWdlbi5hbm5vdGF0ZWQuc21hbGwsUVRMZ2VuZXMsYnk9YygiZWlnZW5fZVFUTF9jYW5kaWRhdGUiPSJuYW1lIikpICU+JQogIHNlbGVjdCguaWQsIHRyYWl0LCBldmVyeXRoaW5nKCkpCmVpZ2VuLnF0bC5jb21iaW5lZApgYGAKCmhvdyBtYW55IFFUTCBoYXZlIGF0IGxlYXN0IHNvbWUgb3ZlcmxhcD8KYGBge3J9CnNvcnQodW5pcXVlKFFUTGdlbmVzJC5pZCkpCnNvcnQodW5pcXVlKGVpZ2VuLnF0bC5jb21iaW5lZCQuaWQpKQpgYGAKCnRocmVlIG9mIGZvdXIKCmFyZSBhbGwgZWlnZW4gZ2VuZXMgb3ZlcmxhcHBpbmc/CgpgYGB7cn0KdW5pcXVlKGVpZ2VuLmFubm90YXRlZC5zbWFsbCR0cmFpdCkKdW5pcXVlKGVpZ2VuLnF0bC5jb21iaW5lZCR0cmFpdCkKYGBgCgpObywgNyBvZiAxMQoKYGBge3J9CndyaXRlX2NzdihlaWdlbi5xdGwuY29tYmluZWQsCiAgICAgICAgICBwYXRoPXN0cl9jKCIuLi9vdXRwdXQvIiwgZmlsZWJhc2UsICJfZWlnZW5RVExfb3ZlcmxhcF8iLCBTeXMuRGF0ZSgpLCAiLmNzdiIpKQpgYGAKCiMjIG92ZXJsYXBzIGFuZCBzaWduaWZpY2FuY2UKCmZpcnN0IGNvbnZlcnQgdGhpbmdzIHRvIHJhbmdlcwpgYGB7cn0KcXRsLmluZm8gPC0gUVRMZ2VuZXMgJT4lCiAgZ3JvdXBfYnkoLmlkKSAlPiUKICBzdW1tYXJpemUoY2hyb209dW5pcXVlKGNocm9tKSxzdGFydD1taW4oc3RhcnQpLGVuZD1tYXgoZW5kKSkKcXRsLmluZm8KcXRsLnJhbmdlcyA8LSBHUmFuZ2VzKHNlcW5hbWVzID0gcXRsLmluZm8kY2hyb20scmFuZ2VzPUlSYW5nZXMoc3RhcnQ9cXRsLmluZm8kc3RhcnQsZW5kPXF0bC5pbmZvJGVuZCkpCnF0bC5yYW5nZXMgPC0gR2Vub21pY1Jhbmdlczo6cmVkdWNlKHF0bC5yYW5nZXMpCmBgYAoKYGBge3J9CmVRVEwucmFuZ2VzIDwtIEdSYW5nZXMoYmF5ZXNpbnQucmVzdWx0JGNociwKICAgICAgICAgICAgICAgICAgICAgICByYW5nZXMgPSBJUmFuZ2VzKHN0YXJ0PWJheWVzaW50LnJlc3VsdCRzdGFydCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuZD1iYXllc2ludC5yZXN1bHQkZW5kKSkKZVFUTC5yYW5nZXMgPC0gR2Vub21pY1Jhbmdlczo6cmVkdWNlKGVRVEwucmFuZ2VzKQpgYGAKCk1ha2UgdGFibGUgb2YgY2hyb21vc29tZSBpbmZvCmBgYHtyfQpjaHIuaW5mbyA8LSBzY2Fub25lX2VpZ2VuLnBoeXMgJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICByb3duYW1lc190b19jb2x1bW4oIm1hcmtlciIpICU+JQogIHNlbGVjdChtYXJrZXIpICU+JQogIHNlcGFyYXRlKG1hcmtlcixpbnRvPWMoImNociIsImJwIiksc2VwPSJ4Iixjb252ZXJ0PVRSVUUpICU+JQogIGdyb3VwX2J5KGNocikgJT4lCiAgc3VtbWFyaXplKHN0YXJ0PW1pbihicCksZW5kPW1heChicCkpCmBgYAoKRG8gdGhlIHNpbXVsYXRpb25zCmBgYHtyfQpzaW1zIDwtIDEwMDAKCnNldC5zZWVkKDIzMjMpCnNpbS5yZXN1bHRzIDwtIHNhcHBseSgxOnNpbXMsIGZ1bmN0aW9uKHMpIHsKICBpZiAocyAlJSAxMDAgPT0gMCkgcHJpbnQocykKICBzaW0uZVFUTCA8LSB0aWJibGUoCiAgICBjaHI9c2FtcGxlKGNoci5pbmZvJGNociwKICAgICAgICAgICAgICAgc2l6ZSA9IGxlbmd0aChlUVRMLnJhbmdlcyksCiAgICAgICAgICAgICAgIHJlcGxhY2UgPSBUUlVFLAogICAgICAgICAgICAgICBwcm9iPWNoci5pbmZvJGVuZC9zdW0oY2hyLmluZm8kZW5kKSksCiAgICB3aWR0aD13aWR0aChlUVRMLnJhbmdlcykgIyB3aWR0aCBvZiB0aGUgUVRMIHRvIHNpbXVsYXRlCiAgKQogIHNpbS5lUVRMIDwtIGNoci5pbmZvICU+JSAKICAgIHNlbGVjdChjaHIsY2hyLnN0YXJ0PXN0YXJ0LGNoci5lbmQ9ZW5kKSAlPiUgcmlnaHRfam9pbihzaW0uZVFUTCxieT0iY2hyIikgI25lZWQgdG8gZ2V0IHRoZSBjaHJvbSBlbmQgc28gd2UgY2FuIHNhbXBsZSBjb3JyZWN0bHkKICBzaW0uZVFUTCA8LSBzaW0uZVFUTCAlPiUgbXV0YXRlKHF0bC5zdGFydCA9IHJ1bmlmKG49bigpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluID0gY2hyLnN0YXJ0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4PSBtYXgoY2hyLnN0YXJ0LGNoci5lbmQtd2lkdGgpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF0bC5lbmQ9cXRsLnN0YXJ0K3dpZHRoKQogIHNpbS5lUVRMLnJhbmdlcyA8LSBHUmFuZ2VzKHNlcW5hbWVzID0gc2ltLmVRVEwkY2hyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmdlcyA9IElSYW5nZXMoc3RhcnQ9c2ltLmVRVEwkcXRsLnN0YXJ0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kPXNpbS5lUVRMJHF0bC5lbmQpKQogIAogIHN1cHByZXNzV2FybmluZ3MocmVzdWx0IDwtIHN1bShjb3VudE92ZXJsYXBzKHF0bC5yYW5nZXMsc2ltLmVRVEwucmFuZ2VzKT4wKSkKICByZXN1bHQKfSkKCmBgYAoKCmBgYHtyfQp0cnVlLm92ZXJsYXAgPC0gc3VtKGNvdW50T3ZlcmxhcHMocXRsLnJhbmdlcyxlUVRMLnJhbmdlcykpICNPSyB0byBpZ25vcmUgd2FybmluZ3MKCnRydWUub3ZlcmxhcAoKbWVhbihzaW0ucmVzdWx0cyA+PSB0cnVlLm92ZXJsYXApCgp0aWJibGUoRlZUUVRMX3ZzX01SZVFUTF9UcnVlX092ZXJsYXBzPXRydWUub3ZlcmxhcCwKICAgICAgIE5fU2ltdWxhdGlvbnNfZmV3ZXJfb3ZlcmxhcHM9c3VtKHNpbS5yZXN1bHRzIDwgdHJ1ZS5vdmVybGFwKSwKICAgICAgIE5fU2ltdWxhdGlvbnNfZ3JlYXRlcl9lcXVhbF9vdmVybGFwcz1zdW0oc2ltLnJlc3VsdHMgPj0gdHJ1ZS5vdmVybGFwKSwKICAgICAgIFBfdmFsdWU9bWVhbihzaW0ucmVzdWx0cyA+PSB0cnVlLm92ZXJsYXApCikgJT4lCiAgd3JpdGVfY3N2KHN0cl9jKCIuLi9vdXRwdXQvIiwgZmlsZWJhc2UsICJfV0dDTkFfZWlnZW5fZVFUTF9zY2Fub25lX292ZXJsYXBfcHZhbF8iLCBTeXMuRGF0ZSgpLCAiLmNzdiIpKQpgYGAKCiMgY2ltCgojIyBQbG90IFFUTAoKYGBge3J9Cgp0aHJlc2hvbGQuOTUgPC0gdGliYmxlKHBlcm0udGhyZXNob2xkPWxvZC50aHJzLmNpbVs1LF0sCiAgICAgICAgICAgICAgICAgICAgICAgdHJhaXQ9Y29sbmFtZXMobG9kLnRocnMuY2ltKSkKCnNjYW5vbmUuZ2F0aGVyIDwtIHNjYW5vbmVfZWlnZW5fY2ltICU+JQogIGdhdGhlcihrZXkgPSB0cmFpdCwgdmFsdWUgPSBMT0QsIC1jaHIsIC1wb3MpICU+JQogIG11dGF0ZShjb25kaXRpb249c3RyX3N1Yih0cmFpdCwxLDIpLCBjb2xvcj1zdHJfc3ViKHRyYWl0LDYsMTAwKSkgJT4lCiAgbGVmdF9qb2luKHRocmVzaG9sZC45NSkKCnNjYW5vbmUuZ2F0aGVyCmBgYAoKYGBge3J9CiAgIHBsLlVOIDwtIHNjYW5vbmUuZ2F0aGVyICU+JSBmaWx0ZXIoY29uZGl0aW9uPT0iVU4iKSAlPiUKICBnZ3Bsb3QoYWVzKHg9cG9zLHk9TE9EKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PXBlcm0udGhyZXNob2xkKSxsdHk9Mixsd2Q9LjUsYWxwaGE9LjUpICsKICBmYWNldF9ncmlkKHRyYWl0IH4gY2hyLCBzY2FsZXM9ImZyZWUiKSArCiAgdGhlbWUoc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTApLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCkpICsKICBnZ3RpdGxlKCJVTiBFaWdlbiBHZW5lIFFUTCIpCnBsLlVOCmdnc2F2ZSgiLi4vb3V0cHV0L2VpZ2VuIGdlbmUgZVFUTCBVTiBDSU0gMjAxMi5wZGYiLHdpZHRoPTEyLGhlaWdodD04KQpgYGAKCgojIyBMb29rIGZvciBvdmVybGFwCgpGb3IgZWFjaCBlaWdlbiBnZW5lLCBmaW5kIFFUTCBib3JkZXJzIGFuZCBsb29rIGZvciBvdmVybGFwIHdpdGggZ3Jvd3RoIFFUTAoKRm9yIGVhY2ggZWlnZW4gZ2VuZSBmaXJzdCBpZGVudGlmeSBjaHJvbW9zb21lcyB3aXRoICJzaWduaWZpY2FudCIgcGVha3MgKGluIHRoaXMgY2FzZSA+IDk5JSBwZXJtdWF0aW9uIHRocmVzaG9sZCkgYW5kIHRoZW4gcnVucyBiYXllc2ludCgpIG9uIHRoZW0gdG8gZGVmaW5lIHRoZSBpbnRlcnZhbHMKCmBgYHtyfQpzaWcuY2hycyA8LSBzY2Fub25lLmdhdGhlciAlPiUgZmlsdGVyKExPRCA+IHBlcm0udGhyZXNob2xkKSAlPiUKICBncm91cF9ieSh0cmFpdCxjaHIpICU+JQogIHN1bW1hcml6ZSh1bmlxdWUoY2hyKSkKc2lnLmNocnMKYGBgCgpub3cgZm9yIGVhY2ggc2lnbmlmaWNhbnQgY2hyb21vc29tZS90cmFpdCBjb21ibyBydW4gYmF5ZXNpbnQKCmBgYHtyfQojcmVtb3ZlIG1hcmtlcnMgd2l0aG91dCBwaHlzaWNhbCBwb3NpdGlvbgpzY2Fub25lX2VpZ2VuX2NpbS5waHlzIDwtIHNjYW5vbmVfZWlnZW5fY2ltWyFzdHJfZGV0ZWN0KHJvd25hbWVzKHNjYW5vbmVfZWlnZW4pLCJeY0EiKSxdCgpiYXllc2ludC5saXN0IDwtIGFwcGx5KHNpZy5jaHJzLDEsZnVuY3Rpb24oaGl0KSB7CiAgICByZXN1bHQgPC0gYmF5ZXNpbnQoc2Nhbm9uZV9laWdlbl9jaW0ucGh5c1tjKCJjaHIiLCJwb3MiLGhpdFsidHJhaXQiXSldLCAKICAgICAgICAgICAgICAgICAgICAgY2hyPWhpdFsiY2hyIl0sIAogICAgICAgICAgICAgICAgICAgICBsb2Rjb2x1bW4gPSAxLAogICAgICAgICAgICAgICAgICAgICBleHBhbmR0b21hcmtlcnMgPSBUUlVFCiAgKQogIGNvbG5hbWVzKHJlc3VsdClbM10gPC0gIkxPRCIKICByZXN1bHQKfSkKCm5hbWVzKGJheWVzaW50Lmxpc3QpIDwtIHNpZy5jaHJzJHRyYWl0CgpiYXllc2ludC5saXN0IDwtIGxhcHBseShiYXllc2ludC5saXN0LGZ1bmN0aW9uKHgpIHggJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICByb3duYW1lc190b19jb2x1bW4odmFyPSJtYXJrZXJuYW1lIikgICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShjaHI9YXMuY2hhcmFjdGVyKGNocikpCikKCmJheWVzaW50LnJlc3VsdCA8LSBhcy50aWJibGUoYmluZF9yb3dzKGJheWVzaW50Lmxpc3QsLmlkPSJ0cmFpdCIpKSAlPiUgCiAgc2VsZWN0KHRyYWl0LGNocixwb3MsbWFya2VybmFtZSxMT0QpICU+JQogIHNlcGFyYXRlKG1hcmtlcm5hbWUsaW50bz1jKCJjaHIxIiwiTWJwIiksc2VwPSJ4IiwgY29udmVydD1UUlVFKSAlPiUKICBncm91cF9ieSh0cmFpdCxjaHIpICU+JSAKICBzdW1tYXJpemUoc3RhcnQ9bWluKE1icCksZW5kPW1heChNYnApLG1pbl9lUVRMX0xPRD1taW4oTE9EKSxtYXhfZVFUTF9MT0Q9bWF4KExPRCkpICU+JSAKICAjZm9yIHRoZSBoaWdoIFFUTCBwZWFrcyB0aGUgaW50ZXJ2YWwgd2lkdGggaXMgMC4gIFRoYXQgaXMgb3Zlcmx5IHByZWNpc2UgYW5kIG5lZWQgdG8gd2lkZW4gdGhvc2UuCiAgbXV0YXRlKHN0YXJ0PWlmZWxzZShzdGFydD09ZW5kLG1heCgwLHN0YXJ0LTIwMDAwKSxzdGFydCksIGVuZD1pZmVsc2Uoc3RhcnQ9PWVuZCxlbmQrMjAwMDAsZW5kKSkKICAKICAKYmF5ZXNpbnQucmVzdWx0CmBgYAoKIyMjIGFubm90YXRlIEVpZ2VuIGdlbmUgUVRMCgpMb2FkIGFubm90YXRpb24KYGBge3J9CkJyYXBhQW5ub3RhdGlvbiA8LSByZWFkX2NzdigiLi4vaW5wdXQvQnJhcGFfVjEuNV9hbm5vdGF0ZWQuY3N2IikKQnJhcGFBbm5vdGF0aW9uCmBgYAoKYGBge3J9CmVpZ2VuLmFubm90YXRlZCA8LSBsYXBwbHkoMTpucm93KGJheWVzaW50LnJlc3VsdCksZnVuY3Rpb24ocm93KSB7CiAgcXRsIDwtIGJheWVzaW50LnJlc3VsdFtyb3csXQogIHJlc3VsdHMgPC0gc3Vic2V0KEJyYXBhQW5ub3RhdGlvbiwgY2hyb209PXF0bCRjaHIgJgogICAgICAgICAgICAgICAgICAgIHN0YXJ0ID49IHF0bCRzdGFydCAmCiAgICAgICAgICAgICAgICAgICAgZW5kIDw9IHF0bCRlbmQpCn0KKQpuYW1lcyhlaWdlbi5hbm5vdGF0ZWQpIDwtIGJheWVzaW50LnJlc3VsdCR0cmFpdAoKZWlnZW4uYW5ub3RhdGVkIDwtIGJpbmRfcm93cyhlaWdlbi5hbm5vdGF0ZWQsLmlkPSJ0cmFpdCIpICU+JQogIG11dGF0ZShjaHJvbT1hcy5jaGFyYWN0ZXIoY2hyb20pKSAlPiUKICBsZWZ0X2pvaW4oYmF5ZXNpbnQucmVzdWx0LGJ5PWMoInRyYWl0IiwiY2hyb20iPSJjaHIiKSkgJT4lICNnZXQgZVFUTCBMT0QKICByZW5hbWUoZWlnZW5fZVFUTF9jYW5kaWRhdGU9bmFtZSkKCmVpZ2VuLmFubm90YXRlZC5zbWFsbCA8LSBlaWdlbi5hbm5vdGF0ZWQgJT4lIHNlbGVjdCh0cmFpdCxlaWdlbl9lUVRMX2NhbmRpZGF0ZSxlbmRzX3dpdGgoIkxPRCIpKQoKZWlnZW4uYW5ub3RhdGVkLnNtYWxsCmBgYAoKZ2l2ZW4gYmF5ZXNpbnQgcmVzdWx0cywgZmluZCBvdmVybGFwcyB3aXRoIFVOIGdyb3d0aCBRVEwKCmBgYHtyfQpmaWxlcGF0aCA8LSAiLi4vaW5wdXQvQWxsMjAxMkhlaWdodFFUTDIueGxzeCIKZmlsZWJhc2UgPC0gZmlsZXBhdGggJT4lIGJhc2VuYW1lKCkgJT4lIHN0cl9yZXBsYWNlKCJcXC4uKiQiLCIiKQoKUVRMZ2VuZXMgPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKGZpbGVwYXRoKVssLTFdClFUTGdlbmVzIDwtIFFUTGdlbmVzICU+JSBkcGx5cjo6cmVuYW1lKC5pZD1RVEwsIEZWVHRyYWl0PUZWVCkgIyBjaGFuZ2UgbmFtZXMgdG8gbWF0Y2ggcHJldmlvdXMgZmlsZQpRVExnZW5lcyA8LSBRVExnZW5lcyAlPiUgZmlsdGVyKHN0cl9kZXRlY3QoRlZUdHJhaXQsIl5VTiIpKQpRVExnZW5lcwpgYGAKCmBgYHtyfQplaWdlbi5xdGwuY29tYmluZWQgPC0gaW5uZXJfam9pbihlaWdlbi5hbm5vdGF0ZWQuc21hbGwsUVRMZ2VuZXMsYnk9YygiZWlnZW5fZVFUTF9jYW5kaWRhdGUiPSJuYW1lIikpICU+JQogIHNlbGVjdCguaWQsIHRyYWl0LCBldmVyeXRoaW5nKCkpCmVpZ2VuLnF0bC5jb21iaW5lZApgYGAKCmhvdyBtYW55IFFUTCBoYXZlIGF0IGxlYXN0IHNvbWUgb3ZlcmxhcD8KYGBge3J9CnVuaXF1ZShRVExnZW5lcyQuaWQpCnVuaXF1ZShlaWdlbi5xdGwuY29tYmluZWQkLmlkKQpgYGAKCnR3byBvZiBmb3VyCgphcmUgYWxsIGVpZ2VuIGdlbmVzIG92ZXJsYXBwaW5nPwoKYGBge3J9CnVuaXF1ZShlaWdlbi5hbm5vdGF0ZWQuc21hbGwkdHJhaXQpCnVuaXF1ZShlaWdlbi5xdGwuY29tYmluZWQkdHJhaXQpCmBgYAoKTm8sIDIKCmBgYHtyfQp3cml0ZV9jc3YoZWlnZW4ucXRsLmNvbWJpbmVkLAogICAgICAgICAgcGF0aD1zdHJfYygiLi4vb3V0cHV0LyIsIGZpbGViYXNlLCAiX2VpZ2VuUVRMX292ZXJsYXBfQ0lNXyIsIFN5cy5EYXRlKCksICIuY3N2IikpCmBgYAoKIyMgb3ZlcmxhcHMgYW5kIHNpZ25maWNhbmNlCgpgYGB7cn0KZVFUTC5yYW5nZXMgPC0gR1JhbmdlcyhiYXllc2ludC5yZXN1bHQkY2hyLAogICAgICAgICAgICAgICAgICAgICAgIHJhbmdlcyA9IElSYW5nZXMoc3RhcnQ9YmF5ZXNpbnQucmVzdWx0JHN0YXJ0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kPWJheWVzaW50LnJlc3VsdCRlbmQpKQplUVRMLnJhbmdlcyA8LSBHZW5vbWljUmFuZ2VzOjpyZWR1Y2UoZVFUTC5yYW5nZXMpCmBgYAoKRG8gdGhlIHNpbXVsYXRpb25zCmBgYHtyfQpzaW1zIDwtIDEwMDAKCnNldC5zZWVkKDQ1NDUpCnNpbS5yZXN1bHRzIDwtIHNhcHBseSgxOnNpbXMsIGZ1bmN0aW9uKHMpIHsKICBpZiAocyAlJSAxMDAgPT0gMCkgcHJpbnQocykKICBzaW0uZVFUTCA8LSB0aWJibGUoCiAgICBjaHI9c2FtcGxlKGNoci5pbmZvJGNociwKICAgICAgICAgICAgICAgc2l6ZSA9IGxlbmd0aChlUVRMLnJhbmdlcyksCiAgICAgICAgICAgICAgIHJlcGxhY2UgPSBUUlVFLAogICAgICAgICAgICAgICBwcm9iPWNoci5pbmZvJGVuZC9zdW0oY2hyLmluZm8kZW5kKSksCiAgICB3aWR0aD13aWR0aChlUVRMLnJhbmdlcykgIyB3aWR0aCBvZiB0aGUgUVRMIHRvIHNpbXVsYXRlCiAgKQogIHNpbS5lUVRMIDwtIGNoci5pbmZvICU+JSAKICAgIHNlbGVjdChjaHIsY2hyLnN0YXJ0PXN0YXJ0LGNoci5lbmQ9ZW5kKSAlPiUgcmlnaHRfam9pbihzaW0uZVFUTCxieT0iY2hyIikgI25lZWQgdG8gZ2V0IHRoZSBjaHJvbSBlbmQgc28gd2UgY2FuIHNhbXBsZSBjb3JyZWN0bHkKICBzaW0uZVFUTCA8LSBzaW0uZVFUTCAlPiUgbXV0YXRlKHF0bC5zdGFydCA9IHJ1bmlmKG49bigpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluID0gY2hyLnN0YXJ0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4PSBtYXgoY2hyLnN0YXJ0LGNoci5lbmQtd2lkdGgpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF0bC5lbmQ9cXRsLnN0YXJ0K3dpZHRoKQogIHNpbS5lUVRMLnJhbmdlcyA8LSBHUmFuZ2VzKHNlcW5hbWVzID0gc2ltLmVRVEwkY2hyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmdlcyA9IElSYW5nZXMoc3RhcnQ9c2ltLmVRVEwkcXRsLnN0YXJ0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kPXNpbS5lUVRMJHF0bC5lbmQpKQogIAogIHN1cHByZXNzV2FybmluZ3MocmVzdWx0IDwtIHN1bShjb3VudE92ZXJsYXBzKHF0bC5yYW5nZXMsc2ltLmVRVEwucmFuZ2VzKT4wKSkKICByZXN1bHQKfSkKCmBgYAoKCmBgYHtyfQp0cnVlLm92ZXJsYXAgPC0gc3VtKGNvdW50T3ZlcmxhcHMocXRsLnJhbmdlcyxlUVRMLnJhbmdlcykpICNPSyB0byBpZ25vcmUgd2FybmluZ3MKCnRydWUub3ZlcmxhcAoKbWVhbihzaW0ucmVzdWx0cyA+PSB0cnVlLm92ZXJsYXApCgp0aWJibGUoRlZUUVRMX3ZzX01SZVFUTF9UcnVlX092ZXJsYXBzPXRydWUub3ZlcmxhcCwKICAgICAgIE5fU2ltdWxhdGlvbnNfZmV3ZXJfb3ZlcmxhcHM9c3VtKHNpbS5yZXN1bHRzIDwgdHJ1ZS5vdmVybGFwKSwKICAgICAgIE5fU2ltdWxhdGlvbnNfZ3JlYXRlcl9lcXVhbF9vdmVybGFwcz1zdW0oc2ltLnJlc3VsdHMgPj0gdHJ1ZS5vdmVybGFwKSwKICAgICAgIFBfdmFsdWU9bWVhbihzaW0ucmVzdWx0cyA+PSB0cnVlLm92ZXJsYXApCikgJT4lCiAgd3JpdGVfY3N2KHN0cl9jKCIuLi9vdXRwdXQvIiwgZmlsZWJhc2UsICJfV0dDTkFfZWlnZW5fZVFUTF9DSU1fb3ZlcmxhcF9wdmFsXyIsIFN5cy5EYXRlKCksICIuY3N2IikpCmBgYAoKCg==